package com.matrix.gradle

import org.gradle.api.GradleException

/**
 * Version management utility class
 */
class VersionUtils {
    
    /**
     * Execute Git command and return output
     */
    static String executeGitCommand(String command, File workingDir = new File('.')) {
        try {
            def process = command.execute(null, workingDir)
            process.waitFor()
            
            if (process.exitValue() == 0) {
                return process.inputStream.text.trim()
            } else {
                def errorOutput = process.errorStream.text.trim()
                throw new GradleException("Git command failed: ${command}. Error: ${errorOutput}")
            }
        } catch (Exception e) {
            throw new GradleException("Failed to execute Git command: ${command}. Error: ${e.message}", e)
        }
    }
    
    /**
     * Check if current directory is a Git repository
     */
    static boolean isGitRepository(File projectDir = new File('.')) {
        try {
            executeGitCommand('git rev-parse --git-dir', projectDir)
            return true
        } catch (Exception e) {
            return false
        }
    }
    
    /**
     * Get the latest version tag
     */
    static String getLatestVersion(File projectDir = new File('.')) {
        try {
            // Get all tags and filter semantic version tags
            def tagsOutput = executeGitCommand('git tag', projectDir)
            
            if (!tagsOutput) {
                return "0.0.0"
            }
            
            def versionTags = tagsOutput.split('\n').findAll { tag ->
                return tag.matches(/^\d+\.\d+\.\d+$/)
            }
            
            if (versionTags.isEmpty()) {
                return "0.0.0"
            }
            
            // Sort versions in descending order to get the latest
            versionTags.sort { a, b ->
                def aParts = a.split('\\.').collect { Integer.parseInt(it) }
                def bParts = b.split('\\.').collect { Integer.parseInt(it) }
                
                for (int i = 0; i < 3; i++) {
                    int aVal = i < aParts.size() ? aParts[i] : 0
                    int bVal = i < bParts.size() ? bParts[i] : 0
                    if (aVal != bVal) {
                        return bVal <=> aVal  // Descending order
                    }
                }
                return 0
            }
            
            return versionTags[0]
        } catch (Exception e) {
            // If no tags exist, return default version
            return "0.0.0"
        }
    }
    
    /**
     * Compare version numbers
     */
    static int compareVersions(String version1, String version2) {
        def v1Parts = version1.split('\\.').collect { Integer.parseInt(it) }
        def v2Parts = version2.split('\\.').collect { Integer.parseInt(it) }
        
        for (int i = 0; i < Math.max(v1Parts.size(), v2Parts.size()); i++) {
            int v1Part = i < v1Parts.size() ? v1Parts[i] : 0
            int v2Part = i < v2Parts.size() ? v2Parts[i] : 0
            
            if (v1Part != v2Part) {
                return v1Part <=> v2Part
            }
        }
        return 0
    }
    
    /**
     * Parse version number
     */
    static Map<String, Integer> parseVersion(String version) {
        def matcher = version =~ /^(\d+)\.(\d+)\.(\d+)$/
        if (!matcher.matches()) {
            throw new GradleException("Invalid version format: ${version}")
        }
        
        return [
            major: Integer.parseInt(matcher[0][1]),
            minor: Integer.parseInt(matcher[0][2]),
            patch: Integer.parseInt(matcher[0][3])
        ]
    }
    
    /**
     * Increment major version
     */
    static String incrementMajor(String version) {
        def parsed = parseVersion(version)
        return "${parsed.major + 1}.0.0"
    }
    
    /**
     * Increment minor version
     */
    static String incrementMinor(String version) {
        def parsed = parseVersion(version)
        return "${parsed.major}.${parsed.minor + 1}.0"
    }
    
    /**
     * Increment patch version
     */
    static String incrementPatch(String version) {
        def parsed = parseVersion(version)
        return "${parsed.major}.${parsed.minor}.${parsed.patch + 1}"
    }
    
    /**
     * Check if tag already exists
     */
    static boolean tagExists(String tagName, File projectDir = new File('.')) {
        try {
            def tagsOutput = executeGitCommand('git tag', projectDir)
            return tagsOutput.split('\n').contains(tagName)
        } catch (Exception e) {
            return false
        }
    }
    
    /**
     * Create Git tag and push to remote repository
     */
    static void createTag(String tagName, String message = null, File projectDir = new File('.')) {
        try {
            // Check if tag already exists
            if (tagExists(tagName, projectDir)) {
                throw new GradleException("Tag '${tagName}' already exists. Please use a different version or delete the existing tag first.")
            }
            
            def tagMessage = message ?: "Release version ${tagName}"
            
            // Create local tag using Git command
            executeGitCommand("git tag -a ${tagName} -m \"${tagMessage}\"", projectDir)
            println "✓ Successfully created local tag: ${tagName}"
            
            // Push tag to remote repository
            try {
                executeGitCommand("git push origin ${tagName}", projectDir)
                println "✓ Successfully pushed tag to remote repository: ${tagName}"
            } catch (Exception pushException) {
                println "⚠️  Tag push failed: ${pushException.message}"
                println "💡 Please manually push tag: git push origin ${tagName}"
                println "💡 Or push all tags: git push origin --tags"
            }
            
        } catch (Exception e) {
            throw new GradleException("Failed to create tag: ${e.message}", e)
        }
    }
    
    /**
     * Create Git branch
     */
    static void createBranch(String branchName, String baseBranch = null, File projectDir = new File('.')) {
        try {
            def command = "git checkout -b ${branchName}"
            if (baseBranch) {
                command = "git checkout -b ${branchName} ${baseBranch}"
            }
            
            executeGitCommand(command, projectDir)
            println "✓ Successfully created branch: ${branchName}"
        } catch (Exception e) {
            throw new GradleException("Failed to create branch: ${e.message}", e)
        }
    }
    
    /**
     * Get next pre-release version number
     */
    static int getNextPreReleaseNumber(String baseVersion, String preReleaseType, File projectDir = new File('.')) {
        try {
            def tagsOutput = executeGitCommand('git tag', projectDir)
            def pattern = "${baseVersion}-${preReleaseType}\\.(\\d+)"
            def maxNumber = 0
            
            if (tagsOutput) {
                tagsOutput.split('\n').each { tag ->
                    def matcher = tag =~ pattern
                    if (matcher.matches()) {
                        def number = Integer.parseInt(matcher[0][1])
                        if (number > maxNumber) {
                            maxNumber = number
                        }
                    }
                }
            }
            
            return maxNumber + 1
        } catch (Exception e) {
            // If no tags exist or command fails, start from 1
            return 1
        }
    }
    
    /**
     * Validate branch name
     */
    static boolean isValidBranchName(String branchName) {
        // Git branch name validation rules
        if (!branchName || branchName.trim().isEmpty()) {
            return false
        }
        
        // Cannot start/end with dot, cannot contain .., cannot contain special characters
        def invalidPatterns = [
            /^\./,           // starts with dot
            /\.$/,           // ends with dot
            /\.\./,          // contains double dots
            /[\s~^:?*\[\]\\]/, // contains special characters
            /@\{/,           // contains @{
            /\/$/,           // ends with slash
            /^\//            // starts with slash
        ]
        
        return !invalidPatterns.any { pattern ->
            branchName.matches(pattern)
        }
    }
}